package com.myproject.mall.user.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.fasterxml.jackson.core.type.TypeReference;
import com.github.yulichang.wrapper.MPJLambdaWrapper;
import com.myproject.mall.common.constants.RedisConstants;
import com.myproject.mall.common.exception.GlobalException;
import com.myproject.mall.common.util.CacheUtil;
import com.myproject.mall.domain.dto.CollectionGroupDTO;
import com.myproject.mall.domain.entity.Collection;
import com.myproject.mall.domain.entity.CollectionGroup;
import com.myproject.mall.domain.entity.Goods;
import com.myproject.mall.domain.holder.UserHolder;
import com.myproject.mall.domain.vo.GoodsVO;
import com.myproject.mall.user.mapper.CollectionGroupMapper;
import com.myproject.mall.user.mapper.CollectionMapper;
import com.myproject.mall.user.service.ICollectionService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.time.LocalDateTime;
import java.util.List;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
import java.util.function.Function;

@Service
@Slf4j
public class CollectionServiceImpl implements ICollectionService {

    @Autowired
    private CollectionMapper collectionMapper;
    @Autowired
    private CollectionGroupMapper groupMapper;
    @Autowired
    private CollectionGroupMapper collectionGroupMapper;
    @Autowired
    private CacheUtil cacheUtil;

    private static final ExecutorService CACHE_REBUILD_EXECUTOR = Executors.newFixedThreadPool(10);

    private List<CollectionGroupDTO> getCollectionDTOs(String userId) {
        MPJLambdaWrapper<CollectionGroup> wrapper = new MPJLambdaWrapper<CollectionGroup>();
        wrapper.eq(CollectionGroup::getUserId, userId)
                .leftJoin(Collection.class, Collection::getGroupId, CollectionGroup::getId)
                .leftJoin(Goods.class, Goods::getId, Collection::getGoodsId)
                .selectCollection(Goods.class, CollectionGroupDTO::getGoodsList, map -> map
                        .id(Goods::getId)
                        .result(Goods::getId, GoodsVO::getId)
                        .result(Goods::getTitle, GoodsVO::getTitle)
                        .result(Goods::getPictures, GoodsVO::getPictures)
                        .result(Goods::getPrice, GoodsVO::getPrice));
        return collectionGroupMapper.selectJoinList(CollectionGroupDTO.class, wrapper);
    }

    private <R, ID> void cacheExecutor(String key, Function<ID, R> dbFallback, ID id, Long time, TimeUnit timeUnit) {
        CACHE_REBUILD_EXECUTOR.submit(() -> {
            try {
                R value = dbFallback.apply(id);
                if(value == null) {
                    cacheUtil.setEmpty(key, time, timeUnit);
                }
                else {
                    cacheUtil.updateCache(key, value, time, timeUnit);
                }
            }
            catch (Exception e) {
                log.error("缓存重建异常", e);
                throw new GlobalException("缓存重建异常：".concat(e.getMessage()));
            }
        });
    }


    @Override
    public List<CollectionGroupDTO> getCollectionGroup() {
        String userId = UserHolder.getUser().getId();
        String key = RedisConstants.USER_COLLECTION_PREFIX + userId;
        return  cacheUtil.getWithLoginExpire(
                key,
                new TypeReference<List<CollectionGroupDTO>>() {},
                this::getCollectionDTOs,
                userId,
                RedisConstants.USER_COLLECTION_EXPIRE,
                TimeUnit.MINUTES);
    }


    @Override
    public void createCollectionGroup(String groupName) {
        String userId = UserHolder.getUser().getId();
        CollectionGroup group = CollectionGroup.builder()
                .userId(userId)
                .title(groupName)
                .createTime(LocalDateTime.now())
                .build();
        groupMapper.insert(group);
        log.info("创建收藏组{}成功", groupName);

        String key = RedisConstants.USER_COLLECTION_PREFIX + userId;
        cacheExecutor(key, this::getCollectionDTOs, userId, RedisConstants.USER_COLLECTION_EXPIRE, TimeUnit.MINUTES);

    }

    @Override
    public void deleteCollectionGroup(Integer groupId) {
        groupMapper.deleteById(groupId);
        collectionMapper.delete(new LambdaQueryWrapper<Collection>().eq(Collection::getGroupId, groupId));
        groupMapper.delete(new LambdaQueryWrapper<CollectionGroup>().eq(CollectionGroup::getId, groupId));
        log.info("删除收藏组{}成功", groupId);

        String userId = UserHolder.getUser().getId();
        String key = RedisConstants.USER_COLLECTION_PREFIX + userId;
        cacheExecutor(key, this::getCollectionDTOs, userId, RedisConstants.USER_COLLECTION_EXPIRE, TimeUnit.MINUTES);
    }

    @Override
    public void addCollection(Long goodsId, Integer groupId) {
        String userId = UserHolder.getUser().getId();
        Collection collection = Collection.builder()
                .userId(userId)
                .goodsId(goodsId)
                .groupId(groupId)
                .createTime(LocalDateTime.now())
                .build();
        collectionMapper.insert(collection);
        log.info("添加商品{}到收藏组{}成功", goodsId, groupId);

        String key = RedisConstants.USER_COLLECTION_PREFIX + userId;
        cacheExecutor(key, this::getCollectionDTOs, userId, RedisConstants.USER_COLLECTION_EXPIRE, TimeUnit.MINUTES);
    }

    @Override
    public void deleteCollection(Long goodsId) {
        String userId = UserHolder.getUser().getId();
        collectionMapper.delete(new LambdaQueryWrapper<Collection>()
                .eq(Collection::getUserId, userId).eq(Collection::getGoodsId, goodsId));
        log.info("删除收藏商品{}成功", goodsId);

        String key = RedisConstants.USER_COLLECTION_PREFIX + userId;
        cacheExecutor(key, this::getCollectionDTOs, userId, RedisConstants.USER_COLLECTION_EXPIRE, TimeUnit.MINUTES);
    }
}
